package au.com.centrumsystems.hudson.plugin.buildpipeline;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

/**
 * @author <a href="mailto:nicolas.deloof@cloudbees.com">Nicolas De loof</a>
 */
public abstract class Grid<T> extends HashMap<Integer, Map<Integer, T>> {

    public T get(int row, int column) {
        return get(row).get(column);
    }

    public void put(int row, int column, T value) {
        get(row).put(column, value);
    }

    public boolean contains(int row, int column) {
        return get(row).containsKey(column);
    }

    /**
     * grid width is the longest column map counting empties (keys represent position, so they are used to determine width)
     */
    public int getWidth() {
        int maxHeight = 0;
        for (final Integer key : keySet()) {
            for (final Integer innerKey : get(key).keySet()) {
                if (maxHeight < innerKey) {
                    maxHeight = innerKey;
                }
            }
        }
        // keys were 0 based
        return maxHeight + 1;
    }

    public boolean isLastColumn(int y) {
        return y + 1 == getWidth();
    }

    public int getHeight() {
        return keySet().size();
    }

    public void createRow(int row) {
        put(row, newRow());
    }

    protected abstract Map<Integer, T> newRow();

    /**
     * Determines the next row of the grid that should be populated. Creates a new row if necessary
     */
    public int getNextAvailableRow(int currentRow, int currentColumn) {
        int nextRow = currentRow;
        boolean nextRoundFound = false;
        // For each row of the grid
        while (!nextRoundFound) {
            final Map<Integer, T> gridRow = get(nextRow);
            if (gridRow != null) {
                if (rowAlreadyContainsData(gridRow, currentColumn)) {
                    nextRow++;
                } else {
                    nextRoundFound = true;
                }
            } else {
                nextRoundFound = true;
            }
        }

        if (get(nextRow) == null) {
            createRow(nextRow);
        }

        return nextRow;
    }

    /**
     * Tests if the row of the grid already contains entries in the columns greater than
     * the entered column.
     * @param rowOfGrid - The row of the grid
     * @param col - The current column of the grid
     * @return - true: The row does contain data in the columns greater than col,
     *          false: The row does not contain data in the columns greater than col
     */
    private boolean rowAlreadyContainsData(Map<Integer, T> rowOfGrid, int col) {
        if (rowOfGrid != null) {
            for (Entry<Integer, T> entry : rowOfGrid.entrySet()) {
                if (entry.getKey() >= col) {
                    if (entry.getValue() != null) {
                        return true;
                    }
                }
            }
            return false;
        } else {
            return false;
        }
    }
}
